package com.lou1052.redis.jedis.ext.client.cluster;

import com.lou1052.redis.jedis.ext.client.single.JedisExt;
import com.lou1052.redis.jedis.ext.client.single.JedisExtPool;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.*;
import redis.clients.jedis.exceptions.JedisConnectionException;

import java.util.Deque;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

import static com.lou1052.redis.jedis.ext.client.cluster.JedisClusterExtInfoCache.getNodeKey;

public abstract class JedisClusterExtConnectionHandler {
  protected final JedisClusterExtInfoCache cache;

  abstract JedisExt getConnection();

  abstract JedisExt getConnectionFromSlot(int slot);

  public JedisExt getConnectionFromNode(HostAndPort node) {
    cache.setNodeIfNotExist(node);
    return cache.getNode(getNodeKey(node)).getResource();
  }

  public JedisClusterExtConnectionHandler(Set<HostAndPort> nodes,
                                          final GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, Supplier<Deque<PooledObject<JedisExt>>> dequeSupplier) {
    this.cache = new JedisClusterExtInfoCache(poolConfig, connectionTimeout, soTimeout,dequeSupplier);
    initializeSlotsCache(nodes, poolConfig);
  }

  public Map<String, JedisExtPool> getNodes() {
    return cache.getNodes();
  }

  /**
   * add by liuqing
   * @param connection
   */
  public void returnConnection(JedisExt connection) {
    cache.getNode(getNodeKey(connection.getClient())).returnResource(connection);
  }

  /**
   * add by liuqing
   * @param connection
   */
  public void returnBrokenConnection(JedisExt connection) {
    cache.getNode(getNodeKey(connection.getClient())).returnBrokenResource(connection);
  }

  /**
   * modify by liuqing
   * @param startNodes
   * @param poolConfig
   */
  private void initializeSlotsCache(Set<HostAndPort> startNodes, GenericObjectPoolConfig poolConfig) {
    for (HostAndPort hostAndPort : startNodes) {
      JedisExt jedis = null;
      try {// modify by liuqing 支持密码
        if (hostAndPort.getPassWord() != null) {
          jedis = new JedisExt(hostAndPort.getHost(), hostAndPort.getPort(), hostAndPort.getPassWord());
        }
      } catch (Exception e) {
      }
      if (jedis == null) {
        jedis = new JedisExt(hostAndPort.getHost(), hostAndPort.getPort());
      }
      try {
        cache.discoverClusterNodesAndSlots(jedis);
        break;
      } catch (JedisConnectionException e) {
        // try next nodes
      } finally {
        if (jedis != null) {
          jedis.close();
        }
      }
    }

    for (HostAndPort node : startNodes) {
      cache.setNodeIfNotExist(node);
    }
  }

  public void renewSlotCache() {
    for (JedisExtPool jp : cache.getNodes().values()) {
      JedisExt jedis = null;
      try {
        jedis = jp.getResource();
        cache.discoverClusterSlots(jedis);
        break;
      } catch (JedisConnectionException e) {
        // try next nodes
      } finally {
        if (jedis != null) {
          jedis.close();
        }
      }
    }
  }

}
